/*
 * MaxLattice.cpp
 *
 *  Created on: Sep 9, 2012
 *      Author: Wanderer
 */

#include "MaxLattice.h"

MaxLattice::MaxLattice(CRF* model, quiver_t* input, value_t* constrain) {
	this->input = input;
	this->constrain = constrain;
	this->length = input->size();
	int numStates = model->getNumStates();
	this->pars = model->getParameters();
	this->inipars = model->getInitialParameters();
	this->finpars = model->getFinalParameters();
	this->weight = new double[numStates];
	this->path = new MaxNode*[numStates];
	this->viterbi = new value_t();
	for (int i = 0; i < numStates; ++i) {
		this->path[i] = new MaxNode();
		this->path[i]->pre = NULL;
		this->path[i]->state = i;
	}
	//initialize forward variables
	for (int i = 0; i < numStates; ++i) {
		weight[i] = (checkPath(0, i)) ? getInitialWeight(i) : NEG_INF;
	}
	//forward
	MaxNode** tnode;
	double tweight;
	for (int ip = 1; ip < length; ++ip) {
		tnode = new MaxNode*[numStates];
		for (int i = 0; i < numStates; ++i) {
			if (checkPath(ip, i)) {
				tnode[i] = new MaxNode();
				tnode[i]->weight = NEG_INF;
				for (int j = 0; j < numStates; ++j) {
					if (path[i]->state == -1)
						continue;
					tweight = this->getWeight(ip, i, j) + weight[j];
					if (tweight > tnode[i]->weight) {
						tnode[i]->weight = tweight;
						tnode[i]->pre = path[j];
						tnode[i]->state = j;
					}
				}
			} else {
				tnode[i]->state = -1;
			}
		}
		delete path;
		path = tnode;
	}
	MaxNode* mpath;
	tweight = NEG_INF;
	for (int i = 0; i < numStates; ++i) {
		if (path[i]->state == -1)
			continue;
		if (path[i]->weight > tweight) {
			mpath = path[i];
		}
	}
	while (mpath != NULL) {
		viterbi->push_back(mpath->state);
		mpath = mpath->pre;
	}
	reverse(viterbi->begin(), viterbi->end());
}

bool MaxLattice::checkPath(int ip, int state) {
	if ((this->constrain == NULL) || (this->constrain->at(ip) == -1)
			|| (state == this->constrain->at(ip))) {
		return true;
	}
	return false;
}

double MaxLattice::getWeight(int ip, int i, int j) {
	value_t* featvec = input->at(ip);
	double weight = 0;
	par_t::iterator it;
	for (unsigned k = 0; k < featvec->size(); ++k) {
		it = pars[j][i].find(featvec->at(k));
		if (it == pars[j][i].end())
			continue;
		weight += (*it).second.value;
	}
	return weight;
}

double MaxLattice::getInitialWeight(int i) {
	value_t* featvec = input->at(0);
	double weight(0.0);
	par_t::iterator it;
	for (unsigned k = 0; k < featvec->size(); ++k) {
		it = inipars->find(featvec->at(k));
		if (it == inipars->end())
			continue;
		weight += inipars->at(featvec->at(k)).value;
	}
	return weight;
}

double MaxLattice::getFinalWeight(int i) {
	value_t* featvec = input->at(length - 1);
	double weight = 0;
	if (finpars == NULL)
		return 0;
	par_t::iterator it;
	for (unsigned k = 0; k < featvec->size(); ++k) {
		it = finpars->find(featvec->at(k));
		if (it == finpars->end())
			continue;
		weight += (*it).second.value;
	}
	return weight;
}

value_t* MaxLattice::best() {
	return this->viterbi;
}

MaxLattice::~MaxLattice() {
// TODO Auto-generated destructor stub
}
